/**
 * 
 */
package name.yzhu.wordsearch;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * a simple two-dimensional array matrix 
 * 
 * @author yhzhu
 *
 */
public class CharMatrix implements Serializable{
    /**
	 * 
	 */
	private static final long serialVersionUID = -4609466450861219109L;
	private char[][] data = null;
	private final int row ;
	private final int column;
	private transient Map<Character, Position> boundaryChar = new HashMap<Character, Position>();
	private transient int curRows = 0;

    /**
     * construct a matrix according with row length and column length
     * @param row   the row numbers
     * @param column   the column numbers
     */
	public CharMatrix(int row, int column) {
		this.row = row;
		this.column = column;
		data = new char[row][column];
	}
    /**
     * add a word to matrix
     * @param word
     */
	public void addWord(String word){
		if (curRows == row)
			throw new IndexOutOfBoundsException("No enough space");
		if (word.length() > column)
			throw new IndexOutOfBoundsException("the word is too long "+word);
		
		for (int i=0; i<word.length(); i++){
			data[curRows][i] = word.charAt(i);
			if (curRows == 0 || curRows == getRow() - 1 
					|| i == 0 || i==word.length() - 1)
				boundaryChar.put(data[curRows][i], new Position(curRows, i));
		}
		curRows++;
	}
	
	/**
	 * check c if it is a boundary letter
	 * @param c
	 * @return
	 */
	public Position getBoundaryCharPosition(char c){
		return boundaryChar.get(c);
	}
    /**
     * return 3 words which is started from point (0, 0)
     * <li>first is from left top to left bottom </li>
     * <li>second is from left top to right top </li>
     * <li>third is from left top to right bottom </li>
     * @return
     */
	public List<CharMatrixWord> getWordsFromTopLeft(){
		List<CharMatrixWord> words = new ArrayList<CharMatrixWord>(3);
		CharMatrixWord word1 = new CharMatrixWord();
		for (int i=0; i<getRow(); i++){
			word1.addChar(data[i][0]);
			word1.addPoint(new Position(i, 0));
		}
		words.add(word1);
		
		CharMatrixWord word2 = new CharMatrixWord();
		for (int j=0; j<getColumn(); j++){
			word2.addChar(data[0][j]);
			word2.addPoint(new Position(0, j));
		}
		words.add(word2);
		
		CharMatrixWord word3 = new CharMatrixWord();
		int min = getRow();
		if (getColumn() < min)
			min = getColumn();
		for (int k=0; k<min; k++){
			word3.addChar(data[k][k]);
			word3.addPoint(new Position(k, k));
		}
		words.add(word3);
		
		return words;
	}
	/**
     * return 2 words which is started from bottom right<br>
     * first is from bottom right to top right, the second is from bottom right to bottom left
     * @return
     */
	public List<CharMatrixWord> getWordsFromBottomRight(){
		List<CharMatrixWord> words = new ArrayList<CharMatrixWord>(2);
		CharMatrixWord word1 = new CharMatrixWord();
		for (int i=getRow()-1; i>=0; i--){
			word1.addChar(data[i][getColumn()-1]);
			word1.addPoint(new Position(i, getColumn()-1));
		}
		words.add(word1);
		
		CharMatrixWord word2 = new CharMatrixWord();
		for (int j=getColumn()-1; j>=0; j--){
			word2.addChar(data[getRow()-1][j]);
			word2.addPoint(new Position(getRow()-1, j));
		}
		words.add(word2);
		
		return words;
	}
	/**
	 * return words started from Coordinates(1, 0) 
	 * @return
	 */
	public List<CharMatrixWord> getWordsFromLeftMiddle(){
		List<CharMatrixWord> words = new ArrayList<CharMatrixWord>(2);
		for(int i=1; i<getRow()-1; i++){
			CharMatrixWord word = new CharMatrixWord();
			for (int j=0; j<getColumn(); j++){
				word.addChar(data[i][j]);
				word.addPoint(new Position(i, j));
			}
			words.add(word);
		}
		return words;
	}
	/**
	 * return words started from Coordinates(0, 1) 
	 * @return
	 */
	public List<CharMatrixWord> getWordsFromTopMiddle(){
		List<CharMatrixWord> words = new ArrayList<CharMatrixWord>(2);
		for (int j=1; j<getColumn()-1; j++){
			CharMatrixWord word = new CharMatrixWord();
			for(int i=0; i<getRow(); i++){
				word.addChar(data[i][j]);
				word.addPoint(new Position(i, j));
			}
			words.add(word);
		}
		return words;
	}
	/**
     * return a words which is from left bottom to right top
     * @return
     */
	public CharMatrixWord getWordsFromLeftBottomToRightTop(){
		int min = getRow();
		if (getColumn() < min)
			min = getColumn();
		CharMatrixWord word = new CharMatrixWord();
		for (int k=0; k<min; k++){
			word.addChar(data[getRow()-k-1][k]);
			word.addPoint(new Position(getRow()-k-1, k));
		}
		return word;
	}
	
	public int getRow() {
		return row;
	}

	public int getColumn() {
		return column;
	}
    
	public int getWordCount(){
		return curRows;
	}
	
	/**
	 * matrix word which include the word and its position
	 * @author yhzhu
	 *
	 */
	public static class CharMatrixWord {
		private StringBuilder word = new StringBuilder();
		private List<Position> points = new ArrayList<Position>();
		
		public CharMatrixWord(){}

		public CharMatrixWord(String word, List<Position> point) {
			super();
			this.word.append(word);
			this.points = point;
		}

		public void addPoint(final Position point){
			this.points.add(point);
		}
		
		public void addChar(char c){
			word.append(c);
		}
		
		public String getWord() {
			return word.toString();
		}

		public void setWord(String word) {
			this.word.delete(0, word.length());
		}

		public List<Position> getPoints() {
			return points;
		}

		public void setPoints(List<Position> points) {
			this.points = points;
		}

		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			for(Position p : points)
				sb.append(p.toString());
			return "CharMatrixWord [word=" + word + ", points=" +sb.toString() + "]";
		}
		
		
	}
}
